using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text.RegularExpressions;

namespace ReportMaintenanceUtility
{
	public class ReportManager
	{
		#region PRIVATE MEMBERS
		private ReportWrapper _activeReport = null;
		private ReportWrapper _template = null;
		private Dictionary<string, ReportWrapper> _reports = null;
		#endregion

		#region PUBLIC PROPERTIES
		public ReportWrapper ActiveReport
		{
			get { return this._activeReport; }
			set { this._activeReport = value; }
		}
		public ReportWrapper Template
		{
			get { return this._template; }
		}
		public Dictionary<string, ReportWrapper> Reports
		{
			get { return this._reports; }
		}
		#endregion

		#region CONSTRUCTOR(S)
		public ReportManager() : this(string.Empty) { }
		public ReportManager(string templateFilename)
		{
			this._reports = new Dictionary<string, ReportWrapper>();
			this.LoadTemplate(templateFilename);
		}
		#endregion

		#region PUBLIC METHODS
		public void LoadTemplate(string templateFilename)
		{
			if (File.Exists(templateFilename))
			{
				Report rpt = null;
				FileInfo fi = new FileInfo(templateFilename);

				try
				{
					using (TextReader tr = fi.OpenText())
					{
						XmlSerializer serializer = new XmlSerializer(typeof(Report));
						rpt = (Report)serializer.Deserialize(tr);
					}
				}
				catch (Exception ex)
				{
					throw new Exception(string.Format("Unable to load specified file: {0}\n{1}", templateFilename, ex.Message));
				}

				this._template = new ReportWrapper(rpt, fi);
			}
		}
		public void AddReport(FileInfo fi, Report report)
		{
			if (!this.Reports.ContainsKey(fi.Name))
			{
				this.Reports.Add(fi.Name, new ReportWrapper(report, fi));
			}
		}
		public void GenerateComparisonReport(string saveAs, DetailLevel dl)
		{
			if (this.Template != null)
			{
				StringBuilder sb = new StringBuilder();
				switch (dl)
				{
					case DetailLevel.ParameterConformityByReport:
						this.CreateParamterConformityReport(sb);
						break;
					case DetailLevel.AttributeConformityByReport:
						this.CreateAttributeConformityReport(sb);
						break;
					case DetailLevel.ParameterConformityAllReports:
						this.CreateParamterConformityAllReports(sb);
						break;
					case DetailLevel.AttributeConformityAllReports:
						this.CreateAttributeConformityAllReports(sb);
						break;
					case DetailLevel.DetailedParameterComparison:
						this.CreateDetailedParameterComparison(sb);
						break;
				}

				try
				{
					File.WriteAllText(saveAs, sb.ToString());
				}
				catch (IOException ex)
				{
					//File.Exists(string.Format("{0}{1}.txt", saveAs
					MessageBox.Show(string.Format("Unable to save report to file!\n{0}", ex.Message));
				}
				catch (Exception ex)
				{
					MessageBox.Show(string.Format("Unable to save report to file!\n{0}", ex.Message));
				}

			}
		}
		public static bool SaveChanges(ReportWrapper rw)
		{
			if (!rw.ReadOnly && rw.IsDirty)
			{
				rw.SaveState();

				try
				{
					XmlSerializer serializer = new XmlSerializer(typeof(Report));
					string reportXML = string.Empty;

					using (MemoryStream aMemStr = new MemoryStream())
					{
						using (XmlTextWriter writer = new XmlTextWriter(aMemStr, null))
						{
							serializer.Serialize(writer, rw.GetSeedObject());
							reportXML = Encoding.UTF8.GetString(aMemStr.ToArray());

							string pattern = @"Parameters!{0}";
							foreach (ChangeLog cl in rw.Log)
							{
								switch (cl.Type)
								{
									case ChangeType.ParameterName:
										reportXML.Replace(string.Format(pattern, cl.OriginalValue), string.Format(pattern, cl.NewValue));
										break;
								}
							}
						}
					}
					using (TextWriter tw = new StreamWriter(rw.Path, false, Encoding.UTF8))
					{
						tw.Write(reportXML);
						tw.Flush();
						tw.Close();
					}
				}
				catch (Exception ex)
				{
					MessageBox.Show(string.Format("Unable to save changes to report {0}!\n{1}", rw.Name, ex.Message));
					return false;
				}
			}
			return true;
		}
		#endregion

		#region PRIVATE METHODS
		private void CreateParamterConformityReport(StringBuilder sb)
		{
			bool pass = true;
			bool match = false;

			//create header row
			sb.Append(Constants.LABEL_REPORT_NAME);
			foreach (string s in this.ActiveReport.Parameters.Keys)
			{
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, s);
			}
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			sb.AppendLine(string.Empty);

			//compare all parameters to template
			sb.Append(this.ActiveReport.Name);
			foreach (Parameter p in this.ActiveReport.Parameters.Values)
			{
				//call each paramters Equal() method, passing in corresponding template parameter...
				if (this.Template.Parameters.ContainsKey(p.Name))
				{
					match = p.EqualTo(this.Template.Parameters[p.Name]);
					if (match)
					{
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_YES);
					}
					else
					{
						pass = false;
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NO);
					}
				}
				else
				{
					pass = false;
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NA);
				}
			}
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (pass ? Constants.LABEL_YES : Constants.LABEL_NO));
			sb.AppendLine(string.Empty);
		}
		private void CreateAttributeConformityReport(StringBuilder sb)
		{
			Parameter t = null;

			//create header row
			sb.Append(Constants.LABEL_REPORT_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PARM_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_DATATYPE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PROMPT);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_HIDDEN);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_MULTIVALUE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_ALLOWNULL);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_ALLOWBLANK);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_VALID_VALUES);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_DEFAULTVALUE);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			sb.AppendLine(string.Empty);

			//compare all parameters to template
			foreach (Parameter p in this.ActiveReport.Parameters.Values)
			{
				sb.Append(this.ActiveReport.Name);
				if (this.Template.Parameters.ContainsKey(p.Name))
				{
					t = this.Template.Parameters[p.Name];
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, p.Name);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.DataType == t.DataType) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (t.Prompt == "*" || (p.Prompt.Equals(t.Prompt, StringComparison.CurrentCultureIgnoreCase))) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.Hidden == t.Hidden) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.MultiValue == t.MultiValue) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.Nullable == t.Nullable) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.AllowBlank == t.AllowBlank) ? Constants.LABEL_YES : Constants.LABEL_NO);
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (CompareTo<ValidValues>.Compare(p.ValidValues, t.ValidValues) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (CompareTo<DefaultValues>.Compare(p.DefaultValue, t.DefaultValue) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, (p.EqualTo(t) ? Constants.LABEL_YES : Constants.LABEL_NO));
					sb.AppendLine(string.Empty);
				}
				else
				{
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, p.Name);
					for (int i = 0; i < 10; i++)
					{
						sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_NA);
					}
					sb.AppendLine(string.Empty);
				}
				t = null;
			}
		}
		private void CreateParamterConformityAllReports(StringBuilder sb)
		{
			bool pass = true;
			bool match = false;
			DataRow dr = null;
			DataTable dt = new DataTable();
			List<string> columnNames = new List<string>();
			List<string> extraColumns = new List<string>();

			//first get list of all paramenter names defined in the parameter dictionary
			foreach (Parameter tp in this.Template.Parameters.Values)
			{
				columnNames.Add(tp.Name);
			}
			columnNames.Sort();
			columnNames.Add("DIVIDER");

			//now find any parameter names not defined in the template
			foreach (ReportWrapper r in this.Reports.Values)
			{
				if (r.Parameters != null)
				{
					foreach (Parameter p in r.Parameters.Values)
					{
						if (string.IsNullOrEmpty(columnNames.Find(delegate(string s) { return s.Equals(p.Name, StringComparison.CurrentCultureIgnoreCase); })))
						{
							if (string.IsNullOrEmpty(extraColumns.Find(delegate(string s) { return s.Equals(p.Name, StringComparison.CurrentCultureIgnoreCase); })))
							{
								extraColumns.Add(p.Name);
							}
						}
					}
				}
			}
			extraColumns.Sort();
			columnNames.AddRange(extraColumns);

			//create data table to contain list of pass/vail values for all parameters across all reports...
			dt.Columns.Add(new DataColumn(Constants.LABEL_REPORT_NAME));
			dt.Columns.Add(new DataColumn(Constants.LABEL_PASSFAIL));
			foreach (string col in columnNames)
			{
				dt.Columns.Add(new DataColumn(col));
			}

			foreach (ReportWrapper r in this.Reports.Values)
			{
				//create a new row for the current report
				dr = dt.NewRow();
				dr[Constants.LABEL_REPORT_NAME] = r.Name;
				pass = true;

				if (r.Parameters != null)
				{
					foreach (Parameter p in r.Parameters.Values)
					{
						match = false;

						//call each paramters Equal() method, passing in corresponding template parameter...
						if (this.Template.Parameters.ContainsKey(p.Name))
						{
							match = p.EqualTo(this.Template.Parameters[p.Name]);
							if (match)
							{
								dr[p.Name] = Constants.LABEL_YES;
							}
							else
							{
								pass = false;
								dr[p.Name] = Constants.LABEL_NO;
							}
						}
						else
						{
							pass = false;
							dr[p.Name] = Constants.LABEL_NA;
						}
					}
				}
				dr[Constants.LABEL_PASSFAIL] = (pass ? Constants.LABEL_YES : Constants.LABEL_NO);
				dr["DIVIDER"] = "!";

				dt.Rows.Add(dr);
			}

			sb.Append(Constants.LABEL_REPORT_NAME);
			sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, Constants.LABEL_PASSFAIL);
			foreach (string s in columnNames)
			{
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, s);
			}
			sb.AppendLine(string.Empty);
			foreach (DataRow row in dt.Rows)
			{
				sb.Append(row[Constants.LABEL_REPORT_NAME]);
				sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, row[Constants.LABEL_PASSFAIL]);

				for (int i = 2; i < dt.Columns.Count; i++)
				{
					sb.AppendFormat(Constants.FORMAT_REPORT_ITEM, row[dt.Columns[i].ColumnName]);
				}
				sb.AppendLine(string.Empty);
			}
		}
		private void CreateAttributeConformityAllReports(StringBuilder sb)
		{
			//todo: complete this report method
		}
		private void CreateDetailedParameterComparison(StringBuilder sb)
		{
			//todo: complete this report method
		}
		#endregion

		public enum DetailLevel
		{
			ParameterConformityByReport,
			AttributeConformityByReport,
			ParameterConformityAllReports,
			AttributeConformityAllReports,
			DetailedParameterComparison
		}
	}
}
